接下來要做最主要的部分就是展開結點,展開結點時,我們分成四大類情形來看,分別為船A在右岸且船B在右岸、船A在左岸且船B在右岸、船A在右岸且船B在左岸、船A在右岸且船B在右岸。而每種情形下又可再細分成接下來開A船、開B船或是開A船且開B船。
我們以雙層巢狀迴圈去展開結點的可能,外層所代表的為開船的總人數,最低為1最高為該船的承載上限人數,內層所代表的為開船的人中傳教士的人數。
只開A船的設計如下:
# sail boatA
for a1 in range(1, A + 1):
for a2 in range(0, a1 + 1):
next_state = state.State(current.m - a2, current.c - (a1 - a2), not current.a, current.b)
next_state.g = current.g + A_COST
next_state.h = state.h(next_state)
next_state.f = next_state.g + next_state.h
boat_state = state.BoatState(a2, a1 - a2, 0, 0)
if state.isLegal(next_state, boat_state):
next_state.num = number
number += 1
next_state.parent = current.num
next_state.boat = boat_state
list.append(next_state)
只開B船的情形和只開A船時相似,兩者之差在船的承載上限不同,因此只須更改一下外層迴圈的便數即可。
只開B船的設計如下:
# sail boatB
for b1 in range(1, B + 1):
for b2 in range(0, b1 + 1):
next_state = state.State(current.m - b2, current.c - (b1 - b2), current.a, not current.b)
next_state.g = current.g + B_COST
next_state.h = state.h(next_state)
next_state.f = next_state.g + next_state.h
boat_state = state.BoatState(0, 0, b2, b1 - b2)
if state.isLegal(next_state, boat_state):
next_state.num = number
number += 1
next_state.parent = current.num
next_state.boat = boat_state
list.append(next_state)
而開A船又開B船的情形,我們使用四層巢狀迴圈做結點展開,概念其實都和上面的相同,只是需要考慮A船和B船一起行駛,大概就是將上面兩部分合併即可。
開A船且開B船設計如下:
# sail boatA and boatB
for a1 in range(1, A + 1):
for a2 in range(0, a1 + 1):
for b1 in range(1, B + 1):
for b2 in range(0, b2 + 1):
next_state = state.State(current.m - a2 - b2, current.c - (a1 - a2) - (b1 - b2), not current.a, not current.b)
next_state.g = current.g + AB_COST
next_state.h = state.h(next_state)
next_state.f = next_state.g + next_state.h
boat_state = state.BoatState(a2, a1 - a2, b2, b1 - b2)
if state.isLegal(next_state, boat_state):
next_state.num = number
number += 1
next_state.parent = current.num
next_state.boat = boat_state
list.append(next_state)
上面三部分只是船A和船B皆在右岸時的結點展開,後面還須加上另外三大類(A船在左岸且B船在右岸、A船在右岸且B船在左岸、A船在左岸且B船在左岸),但概念上都是相同的,因此不再額外花篇幅多加敘述。
Github連結:https://github.com/Ming06-22/Missionaries-cannibals